package com.mingliqiye.minecraft.enchantment.conflict.common;

import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.Serializable;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * <p>自定义时间类
 *
 * @author MingLiPro|Armamem0t
 * @version 1.0
 * @see LocalDateTime
 * @see Date
 * @see ZoneId
 */
@JsonSerialize(using = DateTimeJsonSerializer.class)
@JsonDeserialize(using = DateTimeJsonDeserializer.class)
@JsonView(String.class)
public class DateTime implements Serializable {

    public LocalDateTime getLocalDateTime() {
        return localDateTime;
    }

    public void setLocalDateTime(LocalDateTime localDateTime) {
        this.localDateTime = localDateTime;
    }

    private LocalDateTime localDateTime;
    private ZoneId zoneId = ZoneId.systemDefault();

    DateTime() {
        this(Instant.now());
    }

    DateTime(Instant instant) {
        this(LocalDateTime.ofInstant(instant, ZoneId.systemDefault()));
    }

    DateTime(LocalDateTime localDateTime) {
        this.localDateTime = localDateTime;
    }

    DateTime(Instant instant, ZoneId zoneId) {
        this(LocalDateTime.ofInstant(instant, zoneId));
    }

    DateTime(Date date) {
        this(date.toInstant());
    }

    public static DateTime now() {
        return new DateTime();
    }

    public static DateTime ofCurrentTimeMillis(long time) {
        return new DateTime(Instant.ofEpochMilli(time));
    }

    public static DateTime of(Instant instant) {
        return new DateTime(instant);
    }

    public static String format(Date date, String formater) {
        return format(toLocalDateTime(date), formater);
    }

    public static String format(LocalDateTime date, String formater) {
        return DateTimeFormatter.ofPattern(formater).format(date);
    }

    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    public static String format(LocalDateTime date, Formats formats) {
        return format(date, formats.value);
    }

    public static DateTime parse(String timestr, Formats formater) {
        return parse(timestr, formater.value);
    }

    public static DateTime parse(String timestr, String formater) {
        DateTimeFormatter sdf = DateTimeFormatter.ofPattern(formater);
        return of(LocalDateTime.parse(timestr, sdf));
    }

    public static DateTime of(LocalDateTime localDateTime) {
        return new DateTime(localDateTime);
    }

    @Override
    public String toString() {
        return (
            this.getClass().getName() +
            '@' +
            Integer.toHexString(hashCode()) +
            '(' +
            format(Formats.STANDARD_DATETIME_MILLISECOUND7) +
            ")"
        );
    }

    public String format(Formats formater) {
        return format(localDateTime, formater.value);
    }

    public Instant toInstant() {
        return toInstant(localDateTime);
    }

    public static Instant toInstant(LocalDateTime localDateTime) {
        return toInstant(localDateTime, ZoneId.systemDefault());
    }

    public static Instant toInstant(LocalDateTime localDateTime, ZoneId zoneId) {
        return localDateTime.atZone(zoneId).toInstant();
    }

    public Instant toInstant(ZoneId zoneId) {
        return toInstant(localDateTime, zoneId);
    }

    public Date toDate() {
        return toDate(localDateTime);
    }

    public static Date toDate(LocalDateTime localDateTime) {
        return Date.from(toInstant(localDateTime));
    }

    public String format(String formater) {
        return format(localDateTime, formater);
    }

    public DateTime plusYears(long years) {
        return plusYears(this, years);
    }

    public static DateTime plusYears(DateTime dateTime, long years) {
        dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusYears(years));
        return dateTime;
    }

    public DateTime plusMonths(long months) {
        return plusMonths(this, months);
    }

    public static DateTime plusMonths(DateTime dateTime, long months) {
        dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusMonths(months));
        return dateTime;
    }

    public DateTime plusWeeks(long weeks) {
        return plusWeeks(this, weeks);
    }

    public static DateTime plusWeeks(DateTime dateTime, long weeks) {
        dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusWeeks(weeks));
        return dateTime;
    }

    public DateTime plusDays(long days) {
        return plusDays(this, days);
    }

    public static DateTime plusDays(DateTime dateTime, long days) {
        dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusDays(days));
        return dateTime;
    }

    public DateTime plusHours(long hours) {
        return plusHours(this, hours);
    }

    public static DateTime plusHours(DateTime dateTime, long hours) {
        dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusHours(hours));
        return dateTime;
    }

    public DateTime plusMinutes(long minutes) {
        return plusMinutes(this, minutes);
    }

    public static DateTime plusMinutes(DateTime dateTime, long minutes) {
        dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusMinutes(minutes));
        return dateTime;
    }

    public DateTime plusSeconds(long seconds) {
        return plusSeconds(this, seconds);
    }

    public static DateTime plusSeconds(DateTime dateTime, long seconds) {
        dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusSeconds(seconds));
        return dateTime;
    }

    public DateTime plusNanos(long nanos) {
        return plusNanos(this, nanos);
    }

    public static DateTime plusNanos(DateTime dateTime, long nanos) {
        dateTime.setLocalDateTime(dateTime.getLocalDateTime().plusNanos(nanos));
        return dateTime;
    }

    public ZoneId getZoneId() {
        return zoneId;
    }

    public void setZoneId(ZoneId zoneId) {
        this.zoneId = zoneId;
    }

    public enum Formats {
        STANDARD_DATETIME("yyyy-MM-dd HH:mm:ss"),
        STANDARD_DATETIME_MILLISECOUND7("yyyy-MM-dd HH:mm:ss.SSSSSSS"),
        STANDARD_DATETIME_MILLISECOUND6("yyyy-MM-dd HH:mm:ss.SSSSSS"),
        STANDARD_DATETIME_MILLISECOUND5("yyyy-MM-dd HH:mm:ss.SSSSS"),
        STANDARD_DATETIME_MILLISECOUND4("yyyy-MM-dd HH:mm:ss.SSSS"),
        STANDARD_DATETIME_MILLISECOUND3("yyyy-MM-dd HH:mm:ss.SSS"),
        STANDARD_DATETIME_MILLISECOUND2("yyyy-MM-dd HH:mm:ss.SS"),
        STANDARD_DATETIME_MILLISECOUND1("yyyy-MM-dd HH:mm:ss.S"),
        STANDARD_ISO("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"),
        STANDARD_DATETIME_SECOUND("yyyy-MM-dd HH:mm:ss"),
        STANDARD_DATE("yyyy-MM-dd"),
        ISO8601("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"),
        COMPACT_DATETIME("yyyyMMddHHmmss");

        public String getValue() {
            return value;
        }

        private final String value;

        Formats(String value) {
            this.value = value;
        }
    }
}
